\chapter{Do BioNimbus para o ZooNimbus}
\label{cap:bionimbus_zoonimbus}

Neste capítulo é descrito como a proposta da arquitetura de federação em nuvens
desenvolvida por Saldanha~\cite{hugo2012}, o BioNimbus, apresentado na
Seção~\ref{sec:bionimbus}, foi integrado com o programa Apache ZooKeeper,
apresentado na Seção~\ref{sec:zookeeper} e com o Apache Avro, descrito na
Seção~\ref{sec:avro}, essa integração é explicada explicada na
Seção~\ref{sec:apache-zookeeper} e \ref{sec:apache-avro}, o que possibilitou
transformar o BioNimbus em ZooNimbus, versão descrita
na Seção~\ref{sec:zoonimbus}.

	
\section{BioNimbus}
\label{sec:bionimbus}

	O BioNimbus\cite{bionimbusbook}, uma arquitetura para federação de nuvens computacionais
	híbridas~\cite{hugo2012}, permite a integração e o controle de diferentes
	provedores de infraestrutura com suas ferramentas de bioinformática oferecidas
	como serviço, de maneira transparente, flexível e tolerante a falhas, o que
	significa que provedores independentes, heterogêneos, privados ou públicos de
	nuvens computacionais podem oferecer seus serviços conjuntamente, mantendo suas
	características e políticas internas. Neste
	contexto, o BioNimbus procura oferecer a ilusão de que os recursos
	computacionais disponíveis são ilimitados e que as demandas dos usuários sempre
	serão atendidas.
	
Atualmente, federações de nuvens são implementadas por meio de um {\it middleware}
que permite a interação entre diversas nuvens. A proposta do BioNimbus é
justamente ser esse {\it middleware}, possibilitando a interação entre um ou
mais serviços, hospedados em um ou mais provedores de nuvem.
	
	A Figura~\ref{fig:bionimbus} ilustra a arquitetura que permite que um novo
	provedor seja incluído por meio de um {\it plug-in} de integração que é
	responsável por coletar informações sobre os recursos computacionais
	disponíveis, além de permitir a comunicação entre o provedor e os serviços
	controladores da federação. Esses serviços são implementados no núcleo do
	BioNimbus, e oferecem informações sobre os recursos computacionais, tais como,
	armazenamento de dados, capacidade de processamento e latência na rede.
	
	\begin{figure}[H]
	\centering
	\includegraphics[width=13cm, height=10cm]{imagens/bionimbus.jpg}\\
	\caption{BioNimbus uma Arquitetura de Federação de Nuvens Computacionais para
	Aplicações de Bioinformática (adaptado de~\cite{hugo2012}).}
	\label{fig:bionimbus}
	\end{figure}
	
	Para isso, a arquitetura foi projetada de forma que todos os componentes e
	respectivas funcionalidades sejam bem definidos e divididos, permitindo
	simplicidade e eficiência na inclusão de novos provedores de nuvens.
	
	
\subsection{Arquitetura do BioNimbus}
\label{subsec:arquitetura_bionimbus}

	Como pode ser visto na Figura~\ref{fig:bionimbus}, a arquitetura é composta
	pelos serviços de interação com o usuário (camada de aplicação), por um núcleo
	(camada de núcleo) e os {\it plug-ins} utilizados para a comunicação entre o
	núcleo e a infraestrutura (camada dos provedores de nuvem).
	Esses serviços são responsáveis pela interação do usuário com a federação.
	Dessa forma, o usuário interage com esses serviços, que ficam responsáveis pela
	comunicação com o núcleo e os {\it plug-ins}. A seguir são detalhados todos
	esses serviços.
	
	\subsubsection{{\bf Camada de Aplicação}}
	\label{subsubsec:aplicacao_bionimbus}
		
		A interação com o usuário pode ser feita de várias maneiras: páginas web, linhas
		de comando, interface gráficas, sistemas gerenciados de {\it workflows}, etc.
		Essas maneiras são implementadas por meio de serviços de interação que são
		responsáveis por coletar as ações as quais os usuários desejam fazer, e
		repassá-las para a federação de nuvens. Além disso, é função desta camada
		mostrar informações com os {\it feedback} da execução para os usuários.
		
		Para isso os serviços precisam conectar-se a rede P2P e comunicarem com os
		serviços do núcleo da arquitetura do BioNimbus. Essa comunicação pode realizar
		tarefas tais como listar os arquivos armazenados na federação, fazer {\it
		upload} de um arquivo ou enviar um {\it workflow} para ser executado. Dessa
		forma, nota-se que a interface com o usuário é responsável por interagir com
		os usuários, e redija a comunicação com o núcleo.
		
	\subsubsection{{\bf Camada de Núcleo}}
	\label{subsubsec:nucleo_bionimbus}
	
	O chamado núcleo da arquitetura BioNimbus é responsável pela gerência da
	federação. O núcleo do BioNimbus possui seis serviços principais:
	serviço de descobrimento, serviço de monitoramento, serviço de tolerância a
	falhas, serviço de escalonamento, serviço de segurança e serviço de
	armazenamento. Além destes, possui mais dois controladores: controlador de
	{\it jobs} e controlador de SLA, para exercerem suas funcionalidades, os
	serviços e os controladores também interagem com os demais componentes da
	arquitetura por meio de mensagens enviadas pela rede P2P. Esses serviços
	são~\cite{hugo2012}:
	
	\begin{itemize}
	  
	  \item {\bfseries Serviço de descobrimento:} identifica os provedores de serviços e
		consolida as informações sobre a capacidade de armazenamento, processamento,
		latência de rede e recursos disponíveis. O serviço de descobrimento é
		responsável por identificar os provedores de serviço que fazem parte da
		federação, e consolidar as informações sobre a capacidade de armazenamento e
		processamento, as ferramentas oferecidas, os detalhes sobre os parâmetros de
		execução e os arquivos de entrada e saída.
		
		Para realizar sua função, o serviço de descobrimento envia uma mensagem para
		todos os provedores da federação, os quais respondem com as informações
		referentes à infraestrutura e às ferramentas disponíveis. Para consolidar esses
		dados o serviço de descobrimento mantém uma estrutura de dados que é atualizada
		para cada nova resposta dos provedores. Além disso, o serviço remove dessa
		estrutura os provedores que não respondem as requisições.
				
		Finalmente, tendo realizado a consolidação das informações sobre o estado atual
		da federação de nuvens, o serviço de descobrimento é capaz de atender às
		requisições enviadas pelos demais componentes da federação;
	
		\item {\bfseries Serviço de monitoramento:} verifica se um serviço requisitado está
		disponível no provedor de nuvem, procura por um novo provedor na federação caso
		não esteja disponível, e garante que todas as tarefas de um processo sejam
		realmente executadas. Além disso, informa ao serviço de escalonamento quando
		uma tarefa finaliza sua execução;
	
		\item {\bfseries Serviço de armazenamento:} coordena a estratégia de armazenamento
		dos arquivos consumidos e produzidos pelas tarefas executadas no BioNimbus,
		decidindo sobre a distribuição, replicação e controle de acesso dos arquivos
		entre os diferentes serviços.
		 
		O serviço de armazenamento é responsável por coordenar a estratégia de
		armazenamento dos arquivos a serem consumidos ou produzidos pelas tarefas
		executadas na federação. Para isso, o serviço deve decidir sobre a
		distribuição e a replicação dos arquivos entres os diferentes provedores.
		
		Para realizar esta função o serviço de armazenamento pode comunicar-se com o
		serviço de descobrimento para obter acesso as informações sobre a federação. Com
		isso, o serviço saberá as condições atuais de armazenamento de cada um dos
		provedores que faz parte da federação.
		
		Quando o serviço de armazenamento recebe um pedido para gravar um arquivo, ele
		deve decidir em qual federação o arquivo será, efetivamente, armazenado. Para
		isso uma estratégia de armazenamento é definida, de forma que essa escolha
		receba as informações sobre o arquivo (contidas na classe {\it FileInfo}) e
		retorne o provedor (contido na classe {\it PluginInfo}) no qual o arquivo será
		armazenado.
		
		Além disso, esse serviço mantém uma tabela com os arquivos armazenados na
		federação. Essa tabela é responsável por mapear o identificador de um arquivo ao
		local, e a federação no qual o arquivo se encontra, de fato, armazenado.
	
		\item {\bfseries Serviço de escalonamento:} distribui dinamicamente as tarefas
		entre os provedores de nuvem. Para isso, mantém um registro de todas as tarefas
		alocadas. Além disso, controla a carga de cada provedor da nuvem e redistribui
		as tarefas quando os recursos estão sobrecarregados.
	
		O  serviço de escalonamento é responsável por receber os pedidos de execução
		de tarefas submetidas pelos usuários da federação, mantendo um registro com o
		estado de execução de cada um deles.
		
		Antes da tarefa ser enviada para um provedor ela passa pelo serviço de
		escalonamento, que utiliza uma ou mais política de escalonamento para decidir
		em qual provedor a tarefa deve ser executada. Cada política recebe uma lista de
		tarefas a serem escalonadas, e retorna um mapeamento das tarefas e dos
		respectivos provedores que ficarão responsáveis por executá-la. Para isso, a
		política pode comunicar-se com o serviço de descobrimento para obter acesso às
		informações da federação.
		
		Por tratar-se de uma arquitetura de federação de nuvens, a política de
		escalonamento deve considerar vários aspectos, como por exemplo a latência, o
		tempo na fila de espera de uma tarefa, a capacidade de processamento, os
		serviços disponíveis, etc.
	
		\item {\bfseries Serviço de segurança:} garante a segurança dos usuários do
		sistema, implementando um sistema de {\it login} e não permitindo que um
		usuário acesse pastas e tarefas de outro usuário.
		
		\item {\bfseries Controlador de jobs:} é responsável por fazer a
		ligação entre o núcleo da arquitetura e a camada de aplicação. Uma de suas
		atribuições consiste em realizar o controle de acesso dos usuários que tentam
		acessar a federação para realizar pedidos de execução. O controlador acessa o
		serviço de segurança para fazer uma verificação nas credenciais do usuário.
		Além disso, o controlador é responsável por gerenciar os pedidos dos vários
		usuários, de forma a fazer o controle por usuário, e manter os resultados para
		posterior consulta.
		
		\item {\bfseries Controlador de SLA:} segundo Buyya~\cite{lin2010}, um {\it
		Service-level Agreement} (SLA) é um contrato formal entre provedores de serviço
		e consumidores para garantir que as expectativas de qualidade de serviço do
		usuário sejam atingidas. O controlador de SLA no BioNimbus é responsável por
		implementar o ciclo de vida de um SLA, o qual possui seis passos: descoberta de
		provedores de serviço, definição de SLA, estabelecimento do acordo,
		monitoramento de violação do acordo, término de acordo e aplicação de
		penalidades por violação. Para identificar os parâmetros do acordo, são usados
		{\it templates}. Um {\it template} de SLA representa, entre outras coisas, os
		parâmetros de QoS que um usuário negociou com a arquitetura. Ele preenche esse
		{\it template} por meio da camada de interação com o usuário com os valores
		necessários, os quais podem descrever requisitos funcionais - tais como número
		de núcleos de CPU, frequência de CPU, tamanho de memória, versão mínima de
		aplicações ou tamanho de armazenamento; e não funcionais - como tempo de
		resposta, custo a pagar, taxa de transferência de arquivos, disponibilidade ou
		tempo máximo de execução.
		
		O controlador de SLA tem a responsabilidade de investigar se os requisitos
		especificados no {\it template} preenchido pelo usuário podem ser suportados
		pela federação de nuvens naquele dado momento. Para tomar essa decisão, o
		controlador utiliza os dados de SLA informados pelo {\it plug-in} de
		integração de cada provedor. Para cada pedido de execução feito pelo usuário
		para a arquitetura BioNimbus, a negociação SLA procede da seguinte forma:
	
		\begin{itemize}
		  \item O usuário faz seu pedido com um {\it template} preenchido com os
		  parâmetros de SLA desejados à arquitetura;
		  \item O controlador de SLA cria uma sessão de negociação, se ela ainda não
		  existe, e repassa o pedido de execução para o serviço de monitoramento com
		  os requisitos mínimos extraídos do {\it template};
		  \item O serviço de monitoramento requisita, então, um escalonamento para o
		  escalonador de tarefas repassando aqueles parâmetros, e aguarda sucesso ou
		  falha;
		  \item Em caso de falha, o processo é reiniciado com um novo {\it template}
		  SLA até que se chegue a um acordo.
		\end{itemize} 

	\end{itemize}

Após a negociação de um acordo ter obtido sucesso, o controlador de SLA mantém
a sessão do acordo por meio de um identificador único, retornando-o para o
controlador de jobs, que inclui essa informação na sessão do usuário. Com o
pedido do usuário em execução, é preciso acompanhá-lo de forma que o acordo não
seja violado. Essa é uma das responsabilidades do serviço de monitoramento.
Uma interface realiza a integração destes serviços na arquitetura, e pode ser
utilizada para adicionar novas e não previstas funcionalidades à arquitetura,
sem impactar no seu funcionamento, e aumentando sua flexibilidade. Esta
interface exige que algumas funções operacionais sejam implantadas, como
inicialização, atualização de estado e finalização do serviço, bem como oferece
a sinalização de eventos na rede P2P.

\subsubsection{{\bf Camada de Infraestrutura}}
\label{subsubsec:infra_bionimbus}

Os {\it plug-ins} tem como objetivo serem a interface de comunicação entre o seu
respectivo provedor de serviço e os demais componentes da arquitetura BioNimbus.
Para que a comunicação seja feita com sucesso, o {\it plug-in} precisa mapear as
requisições vindas dos componentes da arquitetura para ações correspondentes a
serem realizadas na infraestrutura do provedor de serviço. Por isso, para cada
tipo de infraestrutura é preciso haver uma implementação diferente do {\it plug-in}.

O {\it plug-in} necessita tratar três tipos de requisição, as quais
são informações sobre a infraestrutura do provedor de serviço, gerenciamento de
tarefas e transferência de arquivos, o último será detalhado na
Subseção~\ref{subsec:bionimbus_armazenamento}.

\subsection{Classes do BioNimbus}
\label{subsec:classes_bionimbus}

A seção anterior mostrou uma visão macro do BioNimbus, porém para compreender
a arquitetura é necessário aprofundar alguns detalhes de implementação, para
isso algumas classes de informações são detalhadas a seguir:

\begin{itemize}
  
  \item {PeerInfo:} informações referentes a um servidor, que é um membro da
  rede P2P. Nessa classe são armazenadas informações, tais como o endereço de
  rede, a latência de rede e o tempo que está {\it on-line} na rede;

  \item {PluginInfo:} informações referentes a um {\it plug-in} e o provedor
  referenciado por ele. Nessa classe são armazenadas informações como o
  {\it PeerInfo} do provedor, a quantidade total de CPUs, a quantidade de CPUs
  livres, a capacidade de armazenamento e o tamanho de armazenamento livre;

  \item {ServiceInfo:} informações referentes a um serviço oferecido por um
  provedor que faz parte da federação. Essa classe armazena informações como o
  nome da ferramenta, os parâmetros de configuração, os arquivos de entrada e os
  arquivos de saída;

  \item {JobInfo:} informações referentes a um trabalho, ou seja, uma
  requisição de execução de um serviço na federação. Nessa classe são
  armazenadas informações como o identificador do serviço solicitado e os
  parâmetros para execução do serviço;

  \item {TaskInfo:} informações referentes a uma tarefa, ou seja, uma
  instância de um trabalho. Essa classe representa uma execução de um trabalho
  em um determinado {\it plug-in}. Nessa classe são armazenadas informações,
  como o {\it JobInfo}, que possui dados sobre o trabalho a ser executado, e o
  {\it PluginInfo} com informações do local em que será executado. Além disso,
  essa classe armazena o estado de execução da tarefa;

  \item {FileInfo:} informações referentes a um arquivo armazenado na
  federação. Nessa classe são armazenadas informações como, por exemplo, o nome
  e o tamanho do arquivo;

  \item {PluginFileInfo:} informações referentes a uma instância de um
  arquivo que pode estar armazenado em vários provedores da federação. Essa
  classe armazena informações como o {\it FileInfo} do arquivo, e o {\it
  PluginInfo} do local onde o arquivo está armazenado.

\end{itemize}

Essas classes armazenam informações essenciais para a execução do BioNimbus, e
são utilizadas na comunicação entre os componentes da arquitetura.

\subsection{Serviço de Armazenamento no BioNimbus}
\label{subsec:bionimbus_armazenamento}

O serviço de armazenamento é responsável por coordenar a estratégia de
armazenamento dos arquivos a serem consumidos ou produzidos pelas tarefas
executadas na arquitetura BioNimbus, decidindo sobre a distribuição e
a replicação dos arquivos entre os diferentes provedores.

Para realizar essa função, primeiramente, o serviço de armazenamento tem acesso
às informações sobre a federação, enviando ao serviço de descobrimento mensagens
do tipo {\it CloudReq}. Com isso, o serviço saberá as condições atuais de
armazenamento de cada um dos provedores que faz parte da federação.

Quando um pedido de armazenamento de arquivo é feito, a decisão do local para
onde efetivamente será feito o {\it upload} do arquivo é realizada pela estratégia
configurada por meio da interface {\it PluginInfo chooseStorage(FileInfo
file)}.

Uma estratégia de armazenamento deve implementar esta interface de forma que o
serviço de armazenamento repasse as informações sobre o arquivo (dados em {\it
FileInfo}) e ela retorne o provedor de infraestrutura (dados em {\it PlugInfo}),
para onde será feito o {\it upload} do arquivo baseado em algoritmo próprio e os dados
obtidos do serviço de descobrimento. Assim, a arquitetura BioNimbus possibilita
a utilização de diferentes estratégias de armazenamento.

Além disso, é esse serviço que mantém uma tabela com os arquivos armazenados na
federação, com seus respectivos identificadores e localização. Essa tabela,
chamada {\it FileTables} pode ser persistida de uma ou mais maneiras. A cada
armazenamento confirmado por um {\it plug-in} de integração, após um {\it
upload} de sucesso, o serviço de armazenamento percorre sua coleção de tabelas,
acionando em cada uma delas a interface, {\it void storeFile(ID id, FileInfo
file, List<PluginFileInfo> pluginFiles)}.
Assim, cada tabela é responsável por mapear o identificador de um arquivo
às suas informações originais e aos locais onde ele está armazenado.
Para realizar essas suas funções, o serviço de armazenamento receberá três
grupos de mensagens, as quais deverá atender. O primeiro grupo trata do
processo de decisão do local de armazenamento de arquivo:

\begin{itemize}
	\item {StoreReq:} requisição de escolha do local de armazenamento de um arquivo. Deve
	conter dados da classe {\it FileInfo} para que sejam usados pela estratégia de armazenamento;
	
	\item {StoreReply:} resposta do serviço de armazenamento indicando o local de armazenamento para
	o {\it peer} que solicitou o armazenamento. Deve conter, além dos dados da requisição, dados da
	classe {\it PluginInfo};
	
	\item {StoreAck:} mensagem enviada por um {\it plug-in} de integração após a
	finalização do {\it upload} de um arquivo à sua infraestrutura. Ela contém
	dados da classe {\it PluginFile-Info} que serão armazenados nas tabelas de
	arquivo do serviço de armazenamento.
\end{itemize}

O segundo grupo de mensagens refere-se às consultas sobre os arquivos
armazenados na federação de nuvens:

\begin{itemize}
  \item {ListReq:} requisição de listagem de arquivos armazenados na
  federação de nuvens. Ela não contém nenhum conteúdo adicional;
  
  \item {ListReply:} resposta do serviço de armazenamento com uma coleção de
  dados da classe {\it File-Info}, que representa a consolidação de todos os
  arquivos mantidos na federação naquele momento.
\end{itemize}

O último grupo de mensagens enviadas ao serviço de armazenamento refere-se à
indicação do local de armazenamento de um arquivo, a partir do qual poderia ser
feito um {\it download}:

\begin{itemize}
  \item {GetReq:} requisição sobre o local de onde pode ser feito o {\it download} de um dado
	arquivo. Deve conter, pelo menos, o identificador único do arquivo;
	
  \item {GetReply:} resposta do serviço de armazenamento com dados da classe
  {\it PluginInfo} sobre o provedor, para onde deve ser enviada uma mensagem do
  tipo {\it FilePrepReq} para iniciar o processo de {\it download}.
\end{itemize}

Para definir estratégias de armazenamento eficientes para uma federação de
nuvens, é necessário, primeiramente, conhecer as
características dos dados. Neste trabalho foram considerados as características
dos dados biológicos, entre elas:

\begin{itemize}
  \item Normalmente, ocupam enormes volumes de armazenamento;
  
  \item Durante as execuções de ferramentas de bioinformática não há
  atualização simultânea de dados por mais de um usuário;
  
  \item Fragmentação e replicação podem ser feitos de diferentes modos,
  dependendo da aplicação; 
  
\end{itemize}

Assim, considerando essas características e o ambiente de federação de nuvens,
uma alternativa é a utilização de bancos de dados NoSQL ({\it Not only SQL}), tais
como o HBase~\cite{hbase2013}, o Cassandra~\cite{cassandra2009} ou o
MongoDB~\cite{mongodb2012}, pois além de suas características específicas para
ambiente em nuvem e de larga escala, possuem características que permitem a
conjugação de dados (arquivos biológicos) com conjuntos de informações
(proveniência de dados).

Para as funcionalidades de replicação e de fragmentação, podem ser projetados
mecanismos que estejam em uma camada acima da infraestrutura de armazenamento.
Tais mecanismos devem levar em conta parâmetros como formato dos dados, tamanho
de armazenamento disponível, taxa de transferência da rede, localização
geográfica, entre outros. Com o intuito de diminuir a quantidade de dados
transferidos entre os membros da federação, e aumentar o paralelismo nas
execuções das aplicações.

Essa arquitetura integrada com os programas Apache
Zookeeper~\cite{zookeeper2010}, como coordenador de tarefas, e Apache
Avro~\cite{avro2012}, como responsável pela comunicação entre os computadores
envolvidos na arquitetura, fizeram com que uma reestruturação no BioNimbus fosse
feita para suprir algumas necessidades no sistema. O Apache ZooKeeper e o Apache
Avro são apresentados na Seção~\ref{sec:zookeeper} e na Seção~\ref{sec:avro}.
		
\section{Apache ZooKeeper}
\label{sec:zookeeper}

ZooKeeper~\cite{zookeeper2010} é um serviço de coordenação distribuída, de
código-aberto para aplicações distribuídas.
ZooKeeper é simples e permite que os processos distribuídos coordenem-se por
meio de um espaço de nomes hierárquico, compartilhado, que é organizado de forma
semelhante a um sistema de arquivos padrão.

Conforme mostra a Figura~\ref{fig:zkservice}, o ZooKeeper é composto por vários
servidores, sendo que um deles é o líder e os outros são chamados de seguidores.
Caso o servidor líder desconecte-se do ZooKeeper, um algoritmo de eleição
é executado novamente para que dentre os servidores restantes, um deles torne-se
o novo líder.

Os dados do ZooKeeper são chamados {\it znodes}, e estes são semelhantes aos
arquivos e diretórios. Ao contrário de um sistema de arquivo comum, que é
projetado para o armazenamento, os dados do ZooKeeper são mantidos na memória, o
que faz com que ele consiga números baixos de latência e uma alta taxa de
transferência.
ZooKeeper é replicado, como os processos distribuídos que coordena, o próprio
ZooKeeper se destina a ser replicado ao longo de um conjunto de
máquinas~\cite{zookeeper2010}.

\begin{figure}
\centering 
\includegraphics[width=10cm, height=8cm]{imagens/zkservice.jpg}\\
\caption{Modelo de Serviço do Apache Zookeeper\cite{zookeeper2010}.}
\label{fig:zkservice}
\end{figure}
	
O ZooKeeper mantêm uma imagem na memória, juntamente com os {\it logs} de
transação e {\it snapshots}, em um armazenamento persistente. Os {\it snapshots}
são imagens que o ZooKeeper faz de todos os {\it znodes} presentes em sua
estrutura. O ZooKeeper escreve os {\it snapshots} no sistema de arquivos, ele
não realiza a exclusão dos {\it snapshots}, isto é uma responsabilidade do
operador do sistema.
	
Os clientes conectam-se a um único servidor ZooKeeper. O cliente mantém uma
conexão TCP através da qual se envia pedidos, recebe respostas, recebe eventos
de {\it watchers}, e envia {\it heartbeats}. Se a conexão TCP entre o cliente e
o servidor cai, o cliente irá se conectar a um servidor diferente. {\it
Heartbeats} são pacotes que o ZooKeeper envia para os servidores, esperando
respostas de cada um. Ele faz isso para saber se todos os servidores estão {\it
on-line} no sistema. O tempo de espera para uma resposta de um servidor é, por
padrão, definido em 2s.
	
As operações no ZooKeeper são ordenadas, são utilizados selos para cada atualização
com um número que reflete a ordem de todas as transações ZooKeeper. Operações
subsequentes podem ser usadas a fim de implementar abstrações de nível superior,
como primitivas de sincronização.
O ZooKeeper é especialmente rápido em cargas de trabalho com ambientes onde
operações de leitura são dominantes. Aplicações ZooKeeper são executadas em
milhares de máquinas, e ele executa melhor, onde as leituras são mais comuns do
que as escritas, em proporções de cerca de 10:1.
	
O espaço de nomes fornecido pelo ZooKeeper é muito parecido com o de um sistema
de arquivos padrão. Um nome é uma sequência de elementos de caminho separados
por uma barra (/). Cada nó em nome do espaço ZooKeeper é identificado por um
caminho. Na Figura~\ref{fig:zknamespace} tem-se um exemplo de como funciona a
hierarquia de {\it Znodes} do ZooKeeper.

\subsection{{\it Znodes} Persistentes e {\it Znodes} Efêmeros}
\label{subsec:zookeeper_nos}	
Cada {\it znode} em um {\it namespace} no ZooKeeper pode ter dados associados a ele,
bem como os seus filhos. É como ter um sistema de arquivos que permite que um
arquivo seja também um diretório. ZooKeeper foi projetado para armazenar dados
de coordenação: informações de {\it status}, configurações, informações de
localização, etc. Os dados armazenados em cada {\it znode} são, geralmente,
pequenos, na faixa de {\it byte} para {\it kilobyte}. Cada {\it znode} armazena
no máximo 1Mb de dados.
		
{\it Znodes} mantêm uma estrutura estatística que inclui números de versão para
alterações de dados, mudanças de lista de controle de acesso ({\it Access
Control List} - ACL  ) e carimbos de tempo, para permitir validações de cache e
atualizações coordenadas. Cada vez que os dados de um {\it znode} são
alterados, o número da versão aumenta. Sempre que um cliente recupera dados
recebe também a versão destes dados.

\begin{figure}
	\centering
	\includegraphics[width=10cm, height=8cm]{imagens/zknamespace.jpg}\\
	\caption{Hierarquia de ZNodes no ZooKeeper.}
	\label{fig:zknamespace}
\end{figure}
	
Os dados armazenados em cada {\it znode} em um {\it namespace} é lido e escrito
atomicamente. Operações de leitura obtêm todos os {\it bytes} de dados
associados com um {\it znode},  e uma operação de gravação substitui todos os
dados. Cada nó tem uma ACL, que restringe quem pode fazer o que com cada {\it
znode}. O ZooKeeper tem também a noção de {\it znodes} efêmeros. Estes {\it
znodes} existem enquanto a sessão que criou o {\it znode} está ativa. Quando a
sessão termina o {\it znode} é excluído.

\subsection{\it Watchers}
\label{subsec:zookeeper_watcher}
ZooKeeper~\cite{zookeeper2010} suporta o conceito de {\it watchers}. Os clientes
podem definir um {\it watcher} em um {\it znode}. Um {\it watcher} é uma espécie
de aviso e serve para dizer que um {\it znode} foi alterado ou deletado no
ZooKeeper. Quem insere o {\it watcher} em um determinado {\it znode} são
clientes que desejam saber da situação deste {\it znode}, sendo assim, quando
esse {\it znode} for deletado ou tiver seus dados alterados, somente quem
solicitou receber um {\it watcher} deste {\it znode} que irá receber o aviso.

{\it Watchers} são inseridos apenas uma vez, ou seja, depois que ele foi disparado
para os clientes que solicitaram receber aviso de um {\it znode}, ele deixa de
existir, sendo então necessário que outro {\it watcher} seja inserido novamente no
mesmo {\it znode}, caso se queira saber das operações futuras que possam
acontecer com ele.

\subsection{Garantias}
\label{subsec:zookeeper_Garantias}
ZooKeeper é muito rápido e simples. Uma vez que o seu objetivo é
ser uma base para a construção de serviços mais complexos, como a sincronização.
ZooKeeper fornece um conjunto de garantias, que são as seguintes~\cite{zookeeper2010}:
		
\begin{itemize}
\item {Consistência sequencial}: atualizações de um cliente serão aplicadas na
ordem em que foram enviadas;
\item {Atomicidade}: atualizações retornam sucesso ou fracasso, ou seja, não há
resultados parciais;
\item {Sistema único de imagem}: todos os clientes vão ter o mesmo ponto de vista do
serviço, pois quando qualquer alteração for feita no ZooKeeper, todos que estão
conectados a ele irão obter uma imagem atualizada dos dados do ZooKeeper com as
alterações feitas, independentemente do servidor em que ele estiver conectado;
\item {Confiabilidade}: uma vez que uma atualização foi aplicada, ele irá
persistir a partir daquele momento, até que um cliente substitua a atualização.
\end{itemize}

\subsection{API Simples}
\label{subsec:zookeeper_API}
Um dos objetivos do ZooKeeper é fornecer uma interface de programação muito
simples. Como resultado, ele suporta apenas as seguintes operações~\cite{zookeeper2010}:
\begin{itemize}
	\item {\it create}: cria um {\it znode} numa localização na árvore do ZooKeeper;
	\item {\it delete}: realiza a exclusão de um {\it znode} no ZooKeeper;
	\item {\it exist}: testa se existe um {\it znode} em um determinado local;
	\item {\it getdata}: faz a leitura dos dados a partir de um {\it znode};
	\item {\it setdata}: grava dados em um {\it znode};
	\item {\it getchildren}: recupera uma lista de filhos de um determinado
	{\it znode};
	\item {\it sync}: sincroniza os dados no ZooKeeper a partir do ponto de vista
	de um cliente.
\end{itemize}
	
\section{Apache Avro}
\label{sec:avro}
Apache Avro~\cite{avro2012} é um sistema de serialização de dados e de chamadas
remotas de procedimentos (RPC - {\it Remote Procedure Call}~\cite{rpc})
desenvolvido dentro do projeto Hadoop\cite{hadoop} da Apache.  Ele é um sistema que oferece:
\begin{itemize}
  \item Estrutura de dados rica;
  \item Formato compacto e rápido de dados binários;
  \item Um {\it container} de arquivos para armazenar dados persistentes;
  \item Integração simples com linguagens dinâmicas. Não é necessária a geração de
  código para ler e escrever arquivos de dados, nem para usar ou implementar
  protocolos RPC. Geração de código funciona como uma opção de otimização, só
  vale a pena implementar para linguagens de tipagem estática.
\end{itemize}

\subsection{Esboços}
\label{subsec:avro_esquemas}
Quando os dados do Avro são lidos, o esboço utilizado na escrita deles estão
sempre presentes com os dados. Isso permite que cada dado escrito não tenha
despesas de valor, tornando a serialização de dados rápida e pequena, pois os
dados, juntos com os seus esboços, são totalmente auto-explicativos,
isto facilita o uso de linguagens de {\it script} dinâmicas.
Se o programa de leitura dos dados espera um esboço
diferente, isso pode ser facilmente resolvido, uma vez que ambos os esboços
estão presentes.

Os esboços Avro são escritos em um arquivo {\it .avdl} que contêm as funções utilizadas
que são chamadas remotamente. Na Figura~\ref{fig:avdl} encontra-se um exemplo de
um arquivo {\it .avdl}.

 		\begin{figure}[H]
					\centering
					\includegraphics[width=13cm, height=7cm]{imagens/avdl.jpg}\\
					\caption{Exemplo de Arquivo {\it .avdl} Utilizado pelo Avro no ZooNimbus.}
					\label{fig:avdl}
		\end{figure}

O arquivo contêm os metadados dos dados que são chamados remotamente. 
Quando os dados Avro são armazenados em um arquivo, seu esboço é armazenado
junto com ele, de forma que os arquivos possam ser processados por qualquer
outro programa futuramente. 

Quando o Avro é usado em RPC, o cliente e o servidor usam trocas de esboços de
dados quando uma conexão é estabelecida. Uma vez que o cliente e o servidor têm
esboços do outro, a correspondência entre os mesmos campos nomeados, campos em
falta, campos extras, etc, podem ser facilmente resolvidas. Logo, esboços Avro
são definidos com JSON~\cite{json}. Isso facilita a implementação em linguagens
que já possuem bibliotecas JSON.
	
\subsection{Comparando o Avro com outros Sistemas}
\label{subsec:avro_outros}
	
O Avro oferece serialização de dados por meio de chamadas RPC,
semelhante a outros sistemas, como Thrift~\cite{thrift2013}, Protocol
Buffers~\cite{protocolbuffers2013}, etc.
Contudo, o Avro difere destes sistemas nos seguintes aspectos fundamentais:
		
\begin{itemize}
\item {Tipagem dinâmica:} Avro não requer que o código seja gerado. Os dados são
sempre acompanhados por um esboço, que permite o processamento completo desses
dados, sem a geração de código, tipos de dados estáticos, etc. Isso facilita a
construção de sistemas de processamento de dados e linguagens de genéricos.
\item {Dados sem etiqueta:} desde que o esboço esteja presente quando os dados
são lidos, informações consideravelmente de menor tipo precisam ser codificadas
com os dados. O Avro fornece um esquema com dados binários que permite que cada
dado seja escrito sem sobrecarga,
resultando um uma codificação de dados mais compacta, e um processamento dos
dados mais rápido.
\item {Atribuição automática de ID's de campos:} quando um esboço muda, o
antigo e o novo estão sempre presentes durante o processamento dos
dados, de modo que as diferenças podem ser resolvidas simbolicamente, usando
nomes de campo.
\end{itemize}

\section{Integração com o Apache ZooKeeper}
\label{sec:apache-zookeeper}
O Apache ZooKeeper~\cite{zookeeper2010} foi integrado ao BioNimbus para fornecer
uma nova forma de troca de informações entre os servidores e clientes. As
funções P2P presentes no BioNimbus foram substituídas pela centralização de
dados oferecida pelo ZooKeeper.
Com o uso do sistema P2P, os servidores realizavam troca de mensagens entre si
por meio da rede P2P, além disso, tinham a necessidade de um servidor ficar
enviando mensagens em {\it broadcast} para conhecer todos os outros servidores
da federação.
Com a integração do ZooKeeper, todos os servidores realizam consultas de dados
dos outros servidores dentro ZooKeeper.

Toda vez que um servidor é iniciado dentro da federação, é criado um {\it znode}
dentro do ZooKeeper com os seus dados. Isto fornece uma alta centralização no
controle dos dados, visto que o ZooKeeper é quem irá armazenar os dados dos
servidores. Assim sendo, quem for consultar ou atualizar estes dados, fará estas
operações dentro do próprio ZooKeeper.

 Um servidor ZooKeeper é iniciado em um servidor e todos os outros comunicam-se
 com o ZooKeeper por meio da porta 2181, conforme mostra a
 Figura~\ref{fig:conect_zookeeper}. Caso o servidor com o serviço ZooKeeper
 caia, um algoritmo de eleição é executado pelo ZooKeeper para escolher qual o
 servidor ZooNimbus que será o novo líder. A partir dai, qualquer mudança de
 dado ou leitura é feita neste servidor. Para consultar os dados que estão
 inseridos é necessário iniciar o cliente do ZooKeeper e se conectar ao servidor
 ZooKeeper.
 
 		\begin{figure}[H]
					\centering
					\includegraphics[width=13cm, height=7cm]{imagens/zookeeper_zoonimbus.jpg}\\
					\caption{Exemplo de Conexão entre o ZooNimbus e o ZooKeeper.}
					\label{fig:conect_zookeeper}
		\end{figure}
		
O ZooNimbus utiliza, com frequência, os {\it watchers} do ZooKeeper, para que
todos os outros servidores fiquem sabendo quais dados de um {\it znode} foram
alterados ou deletados.
Para se ter um controle dos arquivos que são inseridos na federação, o ZooNimbus
cria um {\it znode}, chamado {\it pending saves}. Neste {\it znode} são
armazenados dados de todos os arquivos que possuem uma requisição de {\it
upload} e que irão ser inseridos no ZooNimbus.

As requisições de {\it upload} podem ocorrer por um cliente ou por um servidor.
No caso de uma requisição por parte do cliente, o processo começa com a
requisição de um {\it upload}, logo depois, a política de armazenamento decide o
melhor servidor para receber o arquivo. Neste momento é adicionada na {\it
pending saves}, os dados do arquivo que será enviado ao ZooNimbus, esses dados só
são apagados depois que for verificado que o arquivo foi enviado com sucesso.

No caso da replicação de dados, é adicionado na {\it pending saves}, um {\it
znode} com os dados do arquivo que será replicado. Assim, o servidor que ficar
responsável pela duplicação dos dados irá replicar o arquivo na quantidade de
cópias definidas, após isso ele irá deletar o {\it znode} do arquivo replicado.
A quantidade de cópias é definida no {\it Storage Service}, por meio da variável
{\bf replicationfactor}. Inicialmente, definiu-se duas cópias para cada arquivo,
porém esse valor pode ser facilmente alterado.
O servidor responsável conversa com todos os outros servidores por meio de RPC,
utilizando para isso o Apache Avro. 

\section{Integração com o Apache Avro}
\label{sec:apache-avro}

A comunicação entre os servidores e os clientes no BioNimbus era realizada por meio
de mensagens enviadas pela arquitetura de rede P2P. Esta forma de comunicação
foi substituída pelas chamadas RPC~\cite{rpc}. O Apache Avro~\cite{avro2012}
trabalha com RPCs fornecendo uma interface de comunicação mais simples e robusta
ao ZooNimbus. Como o Avro é um programa que trabalha com recursos de rede para
gerar e mandar as RPCs, ele não consegue garantir que uma chamada seja executada
corretamente, caso ocorra alguma problema na rede no momento em que uma chamada é
realizada. O Avro gerou um arquivo {\it .avdl} dentro do ZooNimbus, onde estão implementados os
métodos que são chamados remotamente, tanto por clientes quanto por servidores.

 		\begin{figure}[H]
					\centering
					\includegraphics[width=12cm, height=8cm]{imagens/avro.jpg}\\
					\caption{Chamadas RPC Passo-a-Passo no ZooNimbus.}
					\label{fig:avro_rpc}
		\end{figure}
        
A Figura~\ref{fig:avro_rpc} mostra, de forma simples, como ocorre a
utilização do Avro no ZooNimbus no caso de uma operação de {\it upload}. Como
mostrado na Figura~\ref{fig:avro_rpc} (1) o cliente faz uma conexão com um
servidor na federação e (2) recebe uma lista com os servidores disponíveis para
receber o arquivo, (3) depois o cliente calcula a latência entre ele e os
servidores e envia uma chamada RPC para o servidor em que ele está conectado com
os dados que ele gerou e que serão utilizados no cálculo da política de
armazenamento. Após esse servidor receber os dados, decide qual o melhor
servidor para colocar o arquivo com base nos cálculos da ZooClouS a ser descrito
no próximo capítulo, (4) e faz uma chamada RPC para este servidor com os dados
do arquivo que o cliente pretende enviar para o ZooNimbus.
Após isso, (5) o cliente abre uma conexão com o servidor de destino e envia o
arquivo.
Todo o processo de cálculo da política e do destino do arquivo é detalhado na
Seção~\ref{sec:politica}.

\section{A Nova Versão do BioNimbus - ZooNimbus}
\label{sec:zoonimbus}
O BioNimbus teve várias modificações na sua estrutura original, que foi proposta
por Hugo Saldanha~\cite{hugo2012}. Porém essas alterações não mudaram os
objetivos e as propostas iniciais da arquitetura de federação em nuvens.
Assim, o principal objetivo do ZooNimbus é a oferta de ferramentas como um
serviço em uma plataforma de federação em nuvens, de maneira transparente,
flexível e tolerante a falhas com grande capacidade de processamento e
armazenamento. Assim, a nova versão, ZooNimbus, continua provendo uma plataforma
que constrói uma federação em nuvem que executa serviços de bioinformática com
recursos heterogêneos, públicos ou privados.

 		\begin{figure}[H]
					\centering
					\includegraphics[width=12cm, height=12cm]{imagens/zoonimbus.jpg}\\
					\caption{Arquitetura de Federação em Nuvens ZooNimbus.}
					\label{fig:zoonimbus}
		\end{figure}

Conforme visto na Figura~\ref{fig:zoonimbus}, os meios de comunicação entre as
nuvens no BioNimbus também foram substituídas.
Assim, as chamadas, que antes eram feitas por meio da arquitetura de rede P2P,
foram substituídas por Chamadas de Procedimento Remotas - RPC~\cite{rpc}. Essa
mudança foi feita através do Avro Apache, que fornece uma forma simples e
robusta de comunicação a arquitetura do ZooNimbus.

A arquitetura do ZooNimbus possui três camadas, na camada de aplicação
encontra-se o usuário, atualmente toda a interação é feita por meio
de linha de comando.
No núcleo encontra-se os serviços do ZooNimbus, da mesma forma que a arquitetura do
BioNimbus~\cite{hugo2012}, todos os serviços foram mantidos. Além disso, os serviços
de armazenamento e de escalonamento comunicam-se com o ZooKeeper, pois
nele estão os dados utilizados por estes serviços para que possam realizar as
tarefas e rotinas implementadas. Na camada inferior, a de infraestrutura,
encontra-se as nuvens integradas ao ZooNimbus. Tanto nuvens públicas ou privadas
podem ser integradas a federação, mostrando que o ZooNimbus é uma arquitetura
bastante heterogênea.

A integração do Apache ZooKeeper foi bastante significativa, pois 
proporcionou um serviço de centralização de dados, o qual ofereceu uma interface
fácil e simples para organizar o acesso aos dados do sistema.
 		
 		\begin{figure}[H]
					\centering
					\includegraphics[width=12cm, height=7cm]{imagens/zookeeper_storage.jpeg}\\
					\caption{Estrutura do ZooKeeper com Relação ao {\it Storage Service}.}
					\label{fig:zookeeper_storage}
		\end{figure}
		
A Figura~\ref{fig:zookeeper_storage} mostra como está organizado a estrutura interna do
ZooKeeper com relação ao {\it Storage Service}. Cada servidor tem uma quantidade
de arquivos x e, para cada arquivo, é criado um {\it znode} ZooKeeper com as suas
informações. As informações salvas nos {\it znodes} dos arquivos são o seu nome,
seu ID, o seu tamanho e uma lista, chamada {\it pluginList}. Nesta {\it
pluginList} são guardados os ID's dos servidores que possuem aquele arquivo
salvo em disco.
Quando um servidor se desconecta da federação, todos recebem um {\it watcher}
avisando que este servidor se desconectou, para que assim possa iniciar a rotina
de tratamento de replicação de arquivos por meio da rotina de tolerância a
falhas da ZooClouS, descrita no próximo capítulo. Cada {\it znode} que possui
informações sobre o arquivo que está inacessível é acessado e o ID do servidor
que se desconectou da federação é retirado da {\it pluginList}.
Em seguida, os arquivos são replicados, e então o ID do novo servidor que recebeu o
arquivo é adicionado na {\it pluginList} deste arquivo. Além disso, também é adicionado um
{\it znode} com as informações ao arquivo no servidor que o recebeu.
		
		
\section{Considerações Finais}
\label{sec:consideracoes_cap3}

	Neste capítulo foi descrita a arquitetura de federação em nuvens, o
	BioNimbus, que sofreu integração de dois programas, essenciais
	para coordenação de tarefas e comunicação entre os computadores envolvidos
	na arquitetura, transformando-se assim numa nova versão, denominada de Zoonimbus.
